﻿using System.Collections.Generic;
using System.Data.SqlTypes;
using System.Linq;
using MbUnit.Framework;
using NHibernate;
using NHibernate.Linq;
using Nvigorate.Repository.Test;
using StructureMap;
using Nvigorate.Extensions;

namespace Nvigorate.Repository
{
    [TestFixture]
    public class RepositoryFixture
    {
        private static IRepository<Person> personRepository;
        private static ISession _session; // pretend I'm not here : p

        [FixtureSetUp]
        public void Setup()
        {
            ObjectFactory.Initialize(i => i.AddRegistry(new TestRegistry()));
            personRepository = ObjectFactory.GetInstance<IRepository<Person>>();

            var Balbo = new Person() { FirstName = "Balbo", LastName = "Baggins", Age = 96, };
            var Mungo = new Person() { FirstName = "Mungo", LastName = "Baggins", Age = 93,  };
            var Bungo = new Person() { FirstName = "Bungo", LastName = "Baggins", Age = 80,  };
            var Drogo = new Person() { FirstName = "Drogo", LastName = "Baggins", Age = 80,  };
            var Bilbo = new Person() { FirstName="Bilbo", LastName="Baggins", Age=131,  };
            var Frodo = new Person() { FirstName="Frodo", LastName="Baggins", Age=53,  };
            var Gandalf = new Person() { FirstName="Gandalf", LastName="Greyhame", Age=int.MaxValue, };
            
            var people = new List<Person>()
                             {
                                 Balbo,
                                 Mungo,
                                 Bungo,
                                 Drogo,
                                 Bilbo,
                                 Frodo,
                                 Gandalf,
                             };

            var bumpkin = new Vehicle() { Make="Shire", Model="Bumpkin", Color="Gray", Year=982 };
            var fattyLumpkin = new Vehicle() { Make="Bombadil", Model="Fatty Lumpkin", Color="Brown", Year=982 };
            var oldBrownCart = new Vehicle() { Make="Crap", Model="Old Brown Cart", Color="Brown", Year=982 };
            var hasufel = new Vehicle() { Make = "Mearas", Model = "Hasufel", Color = "White", Year = 982 };
            var shadowfax = new Vehicle() { Make = "Mearas", Model = "Shadowfax", Color = "Silver", Year = 982 };
            var magicalCart = new Vehicle() { Color = "drab", Make = "Thundin's Magic Carts", Model = "Magic Cart", Year = 1492 };

            Balbo.AddVehicle(bumpkin);
            Mungo.AddVehicle(bumpkin);
            Bungo.AddVehicle(fattyLumpkin);
            Bungo.AddVehicle(oldBrownCart);
            Drogo.AddVehicle(fattyLumpkin);
            Drogo.AddVehicle(oldBrownCart);
            Bilbo.AddVehicle(hasufel);
            Frodo.AddVehicle(hasufel);
            Gandalf.AddVehicle(shadowfax);

            personRepository.Save<Person>(people);

            var vehicleRepository = ObjectFactory.GetInstance<IRepository<Vehicle>>();
            vehicleRepository.Save(magicalCart);

            _session = ObjectFactory.GetInstance<ISessionManager>().GetCurrentSession();
            _session.Flush();
        } 

        [Test]
        public void Repository_FindOne()
        {
            var Frodo = personRepository.FindOne(Select.Where<Person>(p => p.FirstName == "Frodo"));
            Assert.IsNotNull(Frodo, "The Shire is empty!");
            Assert.AreEqual("Frodo", Frodo.FirstName );
            Assert.AreEqual("Baggins", Frodo.LastName);
        }

        [Test]
        public void Repository_Get_UsingId()
        {
            var find = new FindFrodoDTO() {Id = 7};
            var Frodo = personRepository.Get<Person, int>(p => p.Id, find.Id);
            Assert.AreEqual("Gandalf", Frodo.FirstName );
            Assert.AreEqual("Greyhame", Frodo.LastName);
        }

        [Test]
        public void Repository_Get_TheBaggins()
        {
            //var bagginses = personRepository.Find(new SearchCriteria<Person>().Add(p => p.LastName == "Baggins"));
            var bagginses = personRepository.Find(Select.Where<Person>(p => p.LastName == "Baggins"));
            Assert.IsNotEmpty(bagginses, "The Baggins have left the Shire!");
            Assert.AreEqual(6, bagginses.Count, "I fear we have lost a Baggins!");
            Assert.IsTrue(bagginses.Any(p => p.FirstName == "Bilbo" && p.LastName == "Baggins"), "Bilbo is not himself ... because he's someone else!");
            Assert.IsTrue(bagginses.Any(p => p.FirstName == "Frodo" && p.LastName == "Baggins"), "Frodo is not himself ... because he's someone else!");
            Assert.IsTrue(bagginses.Any(p => p.FirstName == "Balbo" && p.LastName == "Baggins"), "Balbo is not himself ... because he's someone else!");
            Assert.IsTrue(bagginses.Any(p => p.FirstName == "Mungo" && p.LastName == "Baggins"), "Mungo is not himself ... because he's someone else!");
            Assert.IsTrue(bagginses.Any(p => p.FirstName == "Bungo" && p.LastName == "Baggins"), "Bungo is not himself ... because he's someone else!");
            Assert.IsTrue(bagginses.Any(p => p.FirstName == "Drogo" && p.LastName == "Baggins"), "Drogo is not himself ... because he's someone else!");
        }

        [Test]
        public void Repository_GetCount_TheBaggins()
        {
            var count = personRepository.GetCount(new SearchCriteria<Person>().Add(p => p.LastName == "Baggins"));
            Assert.AreNotEqual(0, count, "The Baggins have left the Shire!");
            Assert.AreEqual(6, count, "I fear we have lost a Baggins!");
        }

        [Test]
        public void Repository_GetTotal_People()
        {
            var count = personRepository.GetTotal<Person>();
            Assert.AreNotEqual(0, count, "The Shire is empty!");
            Assert.AreEqual(7, count, "Oops! Someone forgot to feed the dragon and he ate a Shireling!");
        }

        [Test]
        public void Repository_Get_All()
        {
            var people = personRepository.GetAll<Person>();

            Assert.AreNotEqual(0, people.Count, "The Shire is empty!");
            Assert.AreEqual(7, people.Count, "Oops! Someone forgot to feed the dragon and he ate a Shireling!");
        }

        [Test]
        public void Repository_Get_All_Paged()
        {
            var page1 = personRepository.GetAll<Person>(1, 3);
            Assert.AreEqual(3, page1.Count);

            var page2 = personRepository.GetAll<Person>(2, 3);
            Assert.AreEqual(3, page2.Count);
            Assert.IsFalse(page2.SequenceEqual(page1));

            var page3 = personRepository.GetAll<Person>(3, 3);
            Assert.AreEqual(1, page3.Count);
            Assert.IsFalse(page3.SequenceEqual(page1));
            Assert.IsFalse(page3.SequenceEqual(page2));
        }

        [Test]
        public void Repository_RidesAMearas_Fetching_Strategy_Test()
        {
            var repository = ObjectFactory.GetInstance<IRepository<IMyOtherCarIsAMearas>>();

            var mearasRiders =
                repository.Find(Select.Where<Person>(p => p.Vehicles.Any(v => v.Make == "Mearas"))).Distinct();
            
            mearasRiders.ForEach(rider => Assert.IsTrue(rider.RidesAMearas()));
        }

        [Test]
        public void Repository_RegisterVehicle_Fetching_Strategy_Test()
        {
            var registerVehicle = new RegisterVehicle() {PersonId = 7, VehicleId = 6};
            var command = ObjectFactory.GetInstance<RegisterVehicleCommand>();

            string expectedMessage = "A new vehicle was successfully registered for Gandalf Greyhame";
            string message = "";

            using(var scope = NotificationScope.Create())
            {
                scope.OnEvent += n => Assert.AreEqual(expectedMessage, n.Message);
                var result = command.Execute(registerVehicle);
                message = result.Notifications[0].Message;
            }

            Assert.AreEqual(expectedMessage, message);
            _session.Flush();
            Repository_Get_Registered_Vehicle();
        }

        [Test]
        public void Repository_Get_Person_With_Two_Brown_Vehicles_Test()
        {
            var matching = personRepository.Find(Select.Where<Person>(p => p.Vehicles.Count >= 2 && p.Vehicles.All(v => v.Color == "Brown")));

            Assert.AreEqual(2, matching.ToList().Count);
        }

        public void Repository_Get_Registered_Vehicle()
        {
            var repository = ObjectFactory.GetInstance<IRepository<Vehicle>>();

            var magicalCart =
                repository.FindOne(Select.Where<Vehicle>(v => v.Model == "Magic Cart"));

            Assert.AreEqual(1, magicalCart.Drivers.Count);
            Assert.AreEqual("Gandalf", magicalCart.Drivers.First().FirstName);
        }
    }

    public class FindFrodoDTO
    {
        public int Id { get; set; }
    }
}
